//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <bsp.h>
#include "uart.h"

EXTERN_C bool_t CDECL BspSetupComPort(
               ComPort comPort, const ComPortSettings *pSettings)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    ComPortSettings defaultSettings = {
        115200,
        ComPortByteSize_Default,
        ComPortParity_Default,
        ComPortStopBits_Default
    };

    if (NULL == pSettings) pSettings = &defaultSettings;

    // Make the line parameter
    uint_t uLineParams = 0;

    switch (pSettings->byteSize) {
        case ComPortByteSize_5:     uLineParams |= LCR_WL_5;    break;
        case ComPortByteSize_6:     uLineParams |= LCR_WL_6;    break;
        case ComPortByteSize_7:     uLineParams |= LCR_WL_7;    break;
        case ComPortByteSize_8:     uLineParams |= LCR_WL_8;    break;
        default: return FALSE;
    }

    switch (pSettings->stopBits) {
        case ComPortStopBits_One:   uLineParams |= LCR_SB_1;    break;
        case ComPortStopBits_One5:  uLineParams |= LCR_SB_1_5;  break;
        case ComPortStopBits_Two:   uLineParams |= LCR_SB_2;    break;
        default: return FALSE;
    }

    switch (pSettings->parity) {
        case ComPortParity_No:      uLineParams |= LCR_NP;      break;
        case ComPortParity_Odd:     uLineParams |= LCR_OP;      break;
        case ComPortParity_Even:    uLineParams |= LCR_EP;      break;
        case ComPortParity_Mark:    uLineParams |= LCR_HP;      break;
        case ComPortParity_Space:   uLineParams |= LCR_LP;      break;
        default: return FALSE;
    }

    // Make the baudrate divisor
    uint_t uBaudDiv = 0;
    switch (pSettings->uBaudRate) {
        case 300:       uBaudDiv = BAUDDIV_300;         break;
        case 1200:      uBaudDiv = BAUDDIV_1200;        break;
        case 2400:      uBaudDiv = BAUDDIV_2400;        break;
        case 4800:      uBaudDiv = BAUDDIV_4800;        break;
        case 9600:      uBaudDiv = BAUDDIV_9600;        break;
        case 19200:     uBaudDiv = BAUDDIV_19200;       break;
        case 38400:     uBaudDiv = BAUDDIV_38400;       break;
        case 57600:     uBaudDiv = BAUDDIV_57600;       break;
        case 115200:    uBaudDiv = BAUDDIV_115200;      break;
        default: return FALSE;
    }

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    // Disable uart interrupt
    Outl(_UART_IER(iobase), IER_UUE);

    while (0 == (Inl(_UART_LSR(iobase)) & LSR_TDRQ));

    // Set baut rate
    uint_t lcr = Inl(_UART_LCR(iobase));
    lcr |= LCR_DLAB;
    Outl(_UART_LCR(iobase), lcr);

    Outl(_UART_DLL(iobase), LOBYTE(uBaudDiv));
    Outl(_UART_DLH(iobase), HIBYTE(uBaudDiv));

    // Set line parameters
    Outl(_UART_LCR(iobase), uLineParams);

    // Enable FIFO
    Outl(_UART_FCR(iobase), FCR_TRFIFOE | FCR_RESETRF | FCR_RESETTF);

    return TRUE;
}

EXTERN_C bool_t CDECL BspWriteComPort(ComPort comPort, uint8_t u8Data)
{
    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    while (0 == (Inl(_UART_LSR(iobase)) & LSR_TEMT));

    Outl(_UART_RBR(iobase), u8Data);

    return TRUE;
}

EXTERN_C bool_t CDECL BspReadComPort(ComPort comPort, uint8_t *pu8Data)
{
    if (NULL == pu8Data) return FALSE;

    if (!COMPORT_AVAILABLE(comPort)) return FALSE;

    const ioport_t iobase = COMPORT_IOBASE(comPort);

    uint_t lsr;

    while (TRUE) {
        lsr = Inb(_UART_LSR(iobase));
        if (lsr & LSR_DR) break;
    }

    // Has errors ?
    if (lsr & LSR_ERRS) return FALSE;

    *pu8Data = Inl(_UART_RBR(iobase));

    return TRUE;
}

EXTERN_C bool_t CDECL TryReadComPort(
               ComPort comPort, uint8_t *pu8Data, uint_t uTimes)
{
    assert(0);
    return FALSE;
}
